#include "mediadecode.h"

MediaDecode::MediaDecode()
{

}

MediaDecode::~MediaDecode() {
    if (ifmt_ctx){
        deInit();
    }
}

int MediaDecode::init(const char *video_file_path,bool isDecodeVideo = true,bool isDecodeAudio = true) {
    this->isDecodeVideo = isDecodeVideo;
    this->isDecodeAudio = isDecodeAudio;
    int ret;

    FILE *videoFile = fopen(video_file_path, "rb");
    if (!videoFile) {
        LOGE("文件不存在！\n");
        return -1;
    }

    ifmt_ctx = avformat_alloc_context();

    ret = avformat_open_input(&ifmt_ctx, video_file_path, nullptr, nullptr);
    if (ret < 0) {
        LOGE("avformat_open_input failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
        return -1;
    }

    ret = avformat_find_stream_info(ifmt_ctx, nullptr);  // 里面还调用解码器去做解码尝试， 怎么知道做了decode，比如
    if (ret < 0) {
        LOGE("avformat_find_stream_info failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
        return -1;
    }
    av_dump_format(ifmt_ctx, 0, video_file_path, 0);


    video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);

    audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
    audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

    printf("audio_index_:%d, video_index_:%d\n", audio_index, video_index);
    if (video_index < 0) {
        LOGE("no audio or no video\n");
        return -1;
    }

    //-----------------视频解码-----------------------------------------------------------------------------
    if (isDecodeVideo) {
        video_codec_ctx_ = avcodec_alloc_context3(nullptr);

        video_avCodecParameters = ifmt_ctx->streams[video_index]->codecpar;

        ret = avcodec_parameters_to_context(video_codec_ctx_, video_avCodecParameters);
        if (ret < 0) {
            LOGE("avcodec_parameters_to_context failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
            return -1;
        }

        const AVCodec *video_codec = avcodec_find_decoder(video_codec_ctx_->codec_id); //作业： 硬件解码
        if (!video_codec) {
            LOGE("avcodec_find_decoder failed\n");
            return -1;
        }

        ret = avcodec_open2(video_codec_ctx_, video_codec, nullptr);
        if (ret < 0) {
            LOGE("avcodec_open2 failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
            return -1;
        }
    }

//    if (isDecodeAudio)
    {
        audio_codec_ctx_ = avcodec_alloc_context3(nullptr);
        if (!audio_codec_ctx_){
            LOGE("audio_codec_ctx_ avcodec_alloc_context3 fail");
        }
        audio_avCodecParameters = ifmt_ctx->streams[audio_index]->codecpar;

        ret = avcodec_parameters_to_context(audio_codec_ctx_, audio_avCodecParameters);
        if (ret < 0) {
            LOGE("avcodec_parameters_to_context failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
            return -1;
        }

        const AVCodec *audio_codec = avcodec_find_decoder(audio_codec_ctx_->codec_id); //作业： 硬件解码
        if (!audio_codec) {
            LOGE("avcodec_find_decoder failed\n");
            return -1;
        }

        ret = avcodec_open2(audio_codec_ctx_, audio_codec, nullptr);
        if (ret < 0) {
            LOGE("avcodec_open2 failed, ret:%d, err2str:%s\n", ret, av_err2str2(ret));
            return -1;
        }
    }

    printf("Init finish");

    av_dump_format(ifmt_ctx, 0, video_file_path, 0);  //输出视频信息
    return 0;
}

int MediaDecode::decode(const std::function<int(AVPacket *,AVFrame *,int, AVRational)> &video_func,
                        const std::function<int(AVPacket *,AVFrame *,int, AVRational)> &audio_func,
                        const std::function<int(void)> &finish_func) {
    int ret = 0;
    dec_frame = av_frame_alloc();
    LOGE("audio_index:%d---video_index:%d--\n",audio_index,video_index);
    while (true) {
        ret = av_read_frame(ifmt_ctx, &packet);
        if (ret < 0) {
            LOGE("av_read_frame failed:%s\n", av_err2str2(ret));
            break;
        }
//        LOGE("data数据：%ld---frame_index:%d\n",sizeof(packet.buf),packet.stream_index);
        if (packet.stream_index == video_index) {
            if (isDecodeVideo) {
                ret = avcodec_send_packet(video_codec_ctx_, &packet);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    LOGE("avcodec_send_packet and send_packet both returned EAGAIN, which is an API violation.\n");
                    continue;
                } else if (ret < 0) {
                    LOGE("Error submitting the packet to the decoder, err:%s\n",
                         av_err2str2(ret));
                    break;
                }
                 av_packet_unref(&packet);
                while (true) {
                    ret = avcodec_receive_frame(video_codec_ctx_, dec_frame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
//                        LOGE("Error video avcodec_receive_frame the packet to the decoder,ret == AVERROR(EAGAIN) || ret == AVERROR_EOF err:%s\n",
//                             av_err2str2(ret));
                        break;
                    } else if (ret < 0) {
                        LOGE("Error video avcodec_receive_frame the packet to the decoder  ret < 0, err:%s\n",
                             av_err2str2(ret));
                        break;
                    }

                    if (dec_frame) {
                        ret = video_func(&packet,dec_frame, packet.stream_index, ifmt_ctx->streams[packet.stream_index]->time_base);
                        if (ret < 0) {
                            LOGE("video_func is failure\n");
                            return -1;
                        }
                    }
                }
            }else {
                ret = video_func(&packet,NULL, packet.stream_index,ifmt_ctx->streams[packet.stream_index]->time_base);
                if (ret < 0) {
                    LOGE("video_func is failure\n");
                    return -1;
                }

                if (isDecodeAudio){
                    continue;
                }
                break;
            }
        } else if (packet.stream_index == audio_index) {
            if (isDecodeAudio) {
                ret = avcodec_send_packet(audio_codec_ctx_, &packet);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    LOGE("audio avcodec_send_packet and send_packet both returned EAGAIN, which is an API violation.\n");
                    continue;
                } else if (ret < 0) {
                    LOGE("audio Error submitting the packet to the decoder, err:%s\n",
                         av_err2str2(ret));
                    break;
                }
                av_packet_unref(&packet);
                while (true) {
                    ret = avcodec_receive_frame(audio_codec_ctx_, dec_frame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                        LOGE("audio avcodec_receive_frame and send_packet both returned EAGAIN, which is an API violation.%s\n",av_err2str2(ret));
                        break;
                    } else if (ret < 0) {
                        LOGE("Error audio avcodec_receive_frame the packet to the decoder, err:%s\n",
                             av_err2str2(ret));
                        break;
                    }

                    if (dec_frame) {
                        ret = audio_func(&packet, dec_frame, packet.stream_index,ifmt_ctx->streams[packet.stream_index]->time_base);
                        if (ret < 0) {
                            LOGE("audio_func is failure\n");
                            return -1;
                        }
                    }

                }
            }else{
                ret = audio_func(&packet, NULL, packet.stream_index,ifmt_ctx->streams[packet.stream_index]->time_base);
                if (ret < 0) {
                    LOGE("audio_func is failure\n");
                    return -1;
                }

                if (isDecodeVideo){
                    continue;
                }
                break;
            }
        }
        av_packet_unref(&packet);
        if(dec_frame){
            av_frame_unref(dec_frame);
        }
    }
    ret = finish_func();
    if (ret < 0){
        LOGE("finish_func is failure\n");
        return -1;
    }
    return 0;
}

void MediaDecode::deInit() {
    if (dec_frame){
        av_frame_unref(dec_frame);
        av_frame_free(&dec_frame);
        dec_frame = nullptr;
    }
    av_packet_unref(&packet);

    if (video_codec_ctx_){
        avcodec_close(video_codec_ctx_);
        avcodec_free_context(&video_codec_ctx_);
        video_codec_ctx_ = nullptr;
    }
    if (audio_codec_ctx_){
        avcodec_close(audio_codec_ctx_);
        avcodec_free_context(&audio_codec_ctx_);
        audio_codec_ctx_ = nullptr;
    }
    if (ifmt_ctx){
        avformat_close_input(&ifmt_ctx);
        avformat_free_context(ifmt_ctx);
        ifmt_ctx = nullptr;
    }
}

AVCodecContext *MediaDecode::getAudioAVCodecContext() {
    return audio_codec_ctx_;
}

AVCodecContext *MediaDecode::getVideoAVCodecContext() {
    return video_codec_ctx_;
}
